const fs = require('fs');
const iconv = require('iconv-lite');
const childProcess = require('child_process');
const {shell} = require('electron').remote;

module.exports = class WindowsHelper {
    static readShortcutFile(shortcutPath) {
        let result = null;
        
        try {
            result = shell.readShortcutLink(shortcutPath);
        } catch(e) {}
        
        if(result) {
            return result;
        }
        
        const  DATAFLAG = {
            HAS_TARGET_ID_LIST: 0x00000001,
            HAS_LINK_INFO: 0x00000002,
            HAS_DESCRIPTION: 0x00000004,
            HAS_RELATIVE_PATH: 0x00000008,
            HAS_WORKING_DIR: 0x00000010,
            HAS_ARGUMENTS: 0x00000020,
            HAS_ICON_LOCATION: 0x00000040,
            IS_UNICODE: 0x00000080,
        };
    
        let toint = function(ary, reverse=false) {
            let result = 0;
        
            if(reverse){
                ary = ary.reverse();
            }
        
            for(let i=0; i<ary.length; i++) {
                result += ary[i] << (i*8);
            }
        
            return result;
        };
    
        let tostring = function(ary) {
            let newAry = [];
        
            if(ary.length % 2 != 0) {
                ary.push(0);
            }
        
            while(ary.length) {
                newAry.push(ary.splice(0,2));
            }
        
            return newAry.map( a => String.fromCharCode(a[0] + (a[1]  << 8 ))).join('');
        };
    
        let parseHeader = function (buf) {
            let headerInfo = buf.splice(0, 76);
            let headerSize = toint(headerInfo.splice(0, 4));
            let guid = headerInfo.splice(0, 16);
            let dataFlag = toint(headerInfo.splice(0, 4));
            let attribute = toint(headerInfo.splice(0, 4));
            let creationDateTime = headerInfo.splice(0, 8);
            let lastAccessDateTime = headerInfo.splice(0, 8);
            let lastModificationDateTime = headerInfo.splice(0, 8);
            let fileSize = toint(headerInfo.splice(0, 4));
            let iconIndex = toint(headerInfo.splice(0, 4));
            let showWindow = toint(headerInfo.splice(0, 4));
            let hotkey = headerInfo.splice(0, 2);
            let reserved1 = headerInfo.splice(0, 2);
            let reserved2 = headerInfo.splice(0, 4);
            let reserved3 = headerInfo.splice(0, 4);
        
            dataFlag = (a => {
                return {
                    hasTargetIdList: !!(a & DATAFLAG.HAS_TARGET_ID_LIST),
                    hasLinkInfo: !!(a & DATAFLAG.HAS_LINK_INFO),
                    hasDescription: !!(a & DATAFLAG.HAS_DESCRIPTION),
                    hasRelativePath: !!(a & DATAFLAG.HAS_RELATIVE_PATH),
                    hasWorkingDir: !!(a & DATAFLAG.HAS_WORKING_DIR),
                    hasArguments: !!(a & DATAFLAG.HAS_ARGUMENTS),
                    hasIconLocation: !!(a & DATAFLAG.HAS_ICON_LOCATION),
                    isUnicode: !!(a & DATAFLAG.IS_UNICODE),
                }
            })(dataFlag);
        
            return {
                headerSize,
                guid,
                dataFlag,
                attribute,
                lastModificationDateTime,
                creationDateTime,
                lastAccessDateTime,
                fileSize,
                iconIndex,
                showWindow,
                hotkey,
                reserved1,
                reserved3,
                reserved2,
            };
        };
    
        function _parseItemIdList(data) {
            return data;
        }
    
        function _getAscii(ary, offset, size) {
            offset = offset ? offset : 0;
            let result = [];
    
            for(let i=offset; i<ary.length; i++) {
                if(size == result.length) {
                    break;
                }
                
                result.push(ary[i]);
            }
    
            return result;
        }
        
        function _getAsciiString(ary, offset) {
            offset = offset ? offset : 0;
            let result = [];
    
            for(let i=offset; i<ary.length; i++) {
                if(ary[i] == 0) {
                    break;
                }
        
                result.push(ary[i]);
            }
            
            return iconv.decode(Buffer.from(result), 'gb2312');
        }
        
        function _parseLinkInfo_relativeLink(data) {
            let originData = data.concat([]);
            let size = toint(data.splice(0, 4));
            let flag = toint(data.splice(0, 4));
            let netNameOffset = toint(data.splice(0, 4));
            let deviceNameOffset = toint(data.splice(0, 4));
            let networkProviderType = 0;
            
            let deviceName = null;
            let netName = _getAsciiString(originData, netNameOffset);
            
            if(flag & 1) {
                deviceName = _getAsciiString(originData, deviceNameOffset);
            }
            
            return {
                netName,
                deviceName,
            };
            
            // if(flag & 2) {
            //     networkProviderType = toint(data.splice(0, 4));
            // }
        }
    
        function _parseLinkInfo(data) {
            let originData = data.concat([]);
        
            //link info size
            let totalSize = toint(data.splice(0, 4));
            let headerSize = toint(data.splice(0, 4));
        
            let flag = toint(data.splice(0, 4));
            let volumeOffset = toint(data.splice(0, 4));
            let localPathOffset = toint(data.splice(0, 4));
            let networkRelativeLinkOffset = toint(data.splice(0, 4));
            let commonPathSubfixOffset = toint(data.splice(0, 4));
        
            let path = null;
            let realPath = null;
            
            if(flag & 1) {
                path = _getAsciiString(originData, localPathOffset);
            } else {
                let size = toint(_getAscii(originData, networkRelativeLinkOffset, 4));
                let relativeInfo = _parseLinkInfo_relativeLink(_getAscii(originData, networkRelativeLinkOffset, size));
                path = _getAsciiString(originData, commonPathSubfixOffset);
    
                if(relativeInfo.netName) {
                    realPath = relativeInfo.netName + '\\' + path;
                }
                
                path = (relativeInfo.deviceName ? relativeInfo.deviceName : relativeInfo.netName) + '\\' + path;
                
            }
            
            return {
                path,
                realPath:realPath ? realPath : path
            };
        }
    
        function _getStringSegment(ary, unicode) {
            let size = toint(ary.splice(0, 2));
        
            if(unicode) {
                size *= 2;
            }
        
            return tostring(ary.splice(0, size));
        }
        
        let contentBuf = fs.readFileSync(shortcutPath);
        let aryBuf = [...contentBuf];
        let header = parseHeader(aryBuf);
        
        let targetIdList = null;
        let linkInfo = {};
        let description = null;
        let relativePath = null;
        let workingDirectory = null;
        let cmdLineArguments = null;
        let iconLocation = null;
        
        if(header.dataFlag.hasTargetIdList) {
            let size = toint(aryBuf.splice(0, 2));
            targetIdList = aryBuf.splice(0, size);
            // targetIdList = _parseItemIdList(targetIdList);
        }
        
        if(header.dataFlag.hasLinkInfo) {
            let sizeAry = aryBuf.splice(0, 4);
            let sizeValue = toint(sizeAry);
            linkInfo = aryBuf.splice(0, sizeValue-4);
            linkInfo = _parseLinkInfo(sizeAry.concat(linkInfo));
        }
        
        if(header.dataFlag.hasDescription) {
            description = _getStringSegment(aryBuf, header.dataFlag.isUnicode);
        }
    
        if(header.dataFlag.hasRelativePath) {
            relativePath = _getStringSegment(aryBuf, header.dataFlag.isUnicode);
        }
    
        if(header.dataFlag.hasWorkingDir) {
            workingDirectory = _getStringSegment(aryBuf, header.dataFlag.isUnicode);
        }
    
        if(header.dataFlag.hasArguments) {
            cmdLineArguments = _getStringSegment(aryBuf, header.dataFlag.isUnicode);
        }
    
        if(header.dataFlag.hasIconLocation) {
            iconLocation = _getStringSegment(aryBuf, header.dataFlag.isUnicode);
        }
        
        return {
            target: linkInfo.path,
            realPath: linkInfo.realPath,
            args:cmdLineArguments,
            icon:iconLocation,
            iconIndex: header.iconIndex,
            description,
            workingDirectory,
        }
    }
    
    static cmd(cmd) {
        return childProcess.execSync(cmd);
    }
    
    static wmic(name, columns, whereStr) {
        whereStr = whereStr ? 'where ' + whereStr:'';
        whereStr = whereStr.replace(/\\/g, '\\\\');
        columns = columns.filter(a => !!a).join(',');
        
        let result = this.cmd(`wmic ${name} ${whereStr} get ${columns} /value 2>nul`);
        
        return result.toString().replace(/\r/g, "").trim().split("\n\n").map(a=>a.trim()).filter(a => !!a).map(
            a => {
                let result = {};
                
                a.split("\n").map(b => {
                    let valary = b.split('=');
                    result[valary.shift()] = valary.join('=');
                });
                
                return result;
                
            }
        );
    }
    
    static wmicProcess(columns, where) {
        return this.wmic('PROCESS', columns, where);
    }
    
    static wmicGetAllProcess() {
        return this.wmicProcess(['ExecutablePath','CommandLine', 'Caption']);
    }
};
